import {operatorEqual, applier, operatorAdd} from './functional.js'
import {every, zip, makeContinuable, map, pipe, reduce, take} from './iterator.js'
import {serializeList} from './serializeList.js'
import type {Type} from './type.js'

export type SimpleFunctionSpecification = {
    name: string
    symbolOffset: number
    parameterTypes: Type[]
    type: Type
}
export type BracketFunctionSpecification = {
    left: string
    right: string
    parameterTypes: Type[]
    type: Type
}
export type FunctionSpecification = SimpleFunctionSpecification | BracketFunctionSpecification

export function areEqual(specification1: FunctionSpecification, specification2: FunctionSpecification) {
    if ('name' in specification1 && (
        !('name' in specification2) ||
        specification1.name != specification2.name ||
        specification1.symbolOffset != specification2.symbolOffset
    ))
        return false

    if ('left' in specification1 && (!('left' in specification2) || specification1.left != specification2.left))
        return false

    return pipe(
        zip(specification1.parameterTypes, specification2.parameterTypes),
        every(applier(operatorEqual)),
    )
}

export function* serialize(specification: FunctionSpecification) {
    if ('left' in specification) {
        yield* serializeBracket(specification)
        return
    }

    const {parameterTypes} = specification
    const parameterIterator = pipe(
        parameterTypes,
        map(({name}) => name),
        makeContinuable,
    )

    const {symbolOffset} = specification

    if (symbolOffset > 1)
        yield '('

    yield* pipe(parameterIterator, take(symbolOffset), serializeList)

    if (symbolOffset > 1)
        yield ')'

    if (symbolOffset > 0)
        yield ' '

    yield specification.name

    if (parameterTypes.length > symbolOffset)
        yield ' '

    if (parameterTypes.length - symbolOffset > 1)
        yield '('

    yield* serializeList(parameterIterator)

    if (parameterTypes.length - symbolOffset > 1)
        yield ')'
}

function* serializeBracket({left, right, parameterTypes}: BracketFunctionSpecification): Iterable<string> {
    yield left
    yield ' '

    yield* pipe(parameterTypes, map(({name}) => name), serializeList)

    yield ' '
    yield right
}

export const stringify = (specification: FunctionSpecification) =>
    pipe(specification, serialize, reduce(operatorAdd))
